/**
 * Copyright (C) 2013 Daniel Gee.
 * See LICENSE.txt for more details.
 */
package com.rl.gui;

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.io.Serializable;

import com.rl.dungeon.Location;

/**
 * Represents a displayable screen within the game that can accept input, process it and change the
 * game state if necessary, then return the next screen that should respond to input. The next
 * screen can either be itself, a subscreen, or it's parent.
 * 
 * @author Daniel Gee
 */
public abstract class Screen implements AppearanceMap, Serializable
{
	/**
	 * Screens are {@link Serializable} so that you can easily save your screen right to disk.
	 */
	private static final long serialVersionUID = 1278538770391913013L;

	/**
	 * This screen's parent, the screen that control should pass to if the current screen has
	 * nothing further to do. The initial screen in the chain has to use <tt>null</tt> of course,
	 * but all other screens should always have a non-null parent.
	 */
	protected final Screen parent;

	/**
	 * Constructs a new screen with the given parent. If a screen needs to use a subscreen to
	 * complete an action, but wants control to return to its parent rather than it, simply assign
	 * the screen's parent to be the subscreen's parent, and the screen in the middle will get
	 * dropped.
	 * 
	 * <p>
	 * <b>Example:</b> The game is playing with the MainScreen, when the SpellScreen is opened and a
	 * spell selected. This spell needs a target, so the SpellScreen can't go back to the MainScreen
	 * right away. Instead, it makes a new TargetingScreen as the return value from
	 * {@link #respondToKeypress(KeyEvent)}, and has the TargetingScreen's parent be the MainScreen,
	 * so that once the spell has been targeted and cast the MainScreen can be returned directly,
	 * without having to bother with the intervening SpellScreen any more.
	 * </p>
	 * 
	 * @param parent
	 *            the parent of the screen being constructed. Should not be <tt>null</tt> unless the
	 *            Screen is the top level Screen.
	 */
	public Screen(Screen parent)
	{
		this.parent = parent;
	}

	/**
	 * By default, a Screen always starts at <tt>Location.of(0,0)</tt>.
	 * 
	 * @see com.rl.gui.AppearanceMap#getDisplayStartLocation(int, int)
	 */
	@Override
	public Location getDisplayStartLocation(int displayWidth, int displayHeight)
	{
		return Location.of(0, 0);
	}

	/**
	 * Processes input from a {@link KeyEvent} and then returns the screen to display next. If a
	 * KeyEvent is passed in that the screen doesn't recognize, the screen should generally return
	 * itself, which will cause the game to simply re-display the same screen a second time,
	 * effectively ignoring the unrecognized key.
	 * 
	 * @param key
	 *            the key of input to respond to.
	 * @return the next screen to display.
	 * @see KeyListener
	 */
	public abstract Screen respondToKeypress(KeyEvent key);

	/**
	 * An <b>optional</b> operation for Screens that want to support mouse input. The
	 * <tt>location</tt> will be the {@link Location} within the Screen that the mouse event was
	 * generated at, while the <tt>event</tt> is the {@link MouseEvent} itself.
	 * 
	 * <p>
	 * By default, nothing happens.
	 * </p>
	 * 
	 * @param event
	 *            the {@link MouseEvent} in question.
	 * @param location
	 *            the {@link Location} within the screen.
	 * @return the new {@link Screen} that should be displayed.
	 * @see MouseListener
	 */
	public Screen respondToMouseAt(MouseEvent event, Location location)
	{
		return this;
	}

	/**
	 * An <b>optional</b> operation for Screens that want to support mouse motion. The
	 * <tt>location</tt> will be the {@link Location} within the Screen that the mouse is currently
	 * within, while the <tt>event</tt> is the {@link MouseEvent} itself.
	 * 
	 * <p>
	 * By default, nothing happens.
	 * </p>
	 * 
	 * @param event
	 *            the {@link MouseEvent} in question.
	 * @param location
	 *            the {@link Location} within the screen.
	 * @return the new {@link Screen} that should be displayed.
	 * @see MouseMotionListener
	 */
	public Screen respondToMouseMotionAt(MouseEvent event, Location location)
	{
		return this;
	}

	/**
	 * An <b>optional</b> operation for Screens that want to support the mouse wheel.
	 * 
	 * <p>
	 * By default, nothing happens.
	 * </p>
	 * 
	 * @param event
	 *            the {@link MouseWheelEvent} to respond to.
	 * @return the new {@link Screen} that should be displayed.
	 * @see MouseWheelListener
	 */
	public Screen respondToMouseWheel(MouseWheelEvent event)
	{
		return this;
	}

	/**
	 * The parent of this screen. If a screen isn't returning itself as the result of
	 * {@link #respondToKeypress(KeyEvent)}, and it's not returning a subscreen of some kind, then
	 * it should return its parent.
	 * 
	 * @return the parent
	 */
	protected Screen getParent()
	{
		return this.parent;
	}
}
